﻿using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Xml.XPath;
using System.Security.Cryptography;
using SharpCompress.Archive;

namespace ComicsEditor
{
    public class ReaderPackage : IComicReader
    {
        public readonly string PackageName;

        private IArchive archive;
        private SortedList<string, IArchiveEntry> files;
        private SortedList<string, IArchiveEntry> images;

        public ReaderPackage(string PackageName)
        {
            this.PackageName = PackageName;
            archive = ArchiveFactory.Open(PackageName);

            files = new SortedList<string, IArchiveEntry>(StringComparer.CurrentCultureIgnoreCase);
            images = new SortedList<string, IArchiveEntry>(StringComparer.CurrentCultureIgnoreCase);
            foreach (IArchiveEntry entry in archive.Entries)
            {
                files.Add(entry.FilePath, entry);

                string ext = Path.GetExtension(entry.FilePath);
                int i = 0;
                for (i = 0; i < ComicInfo.Extensions.Length && ext != ComicInfo.Extensions[i]; i++);
                if (i < ComicInfo.Extensions.Length)
                    images.Add(entry.FilePath, entry);
            }
        }

        public XPathDocument GetInfo()
        {
            if (files.ContainsKey("ComicInfo.xml"))
            {
                MemoryStream stream = new MemoryStream();
                files["ComicInfo.xml"].WriteTo(stream);
                stream.Position = 0;
                return new XPathDocument(stream);
            }

            StringBuilder sb = new StringBuilder();
            sb.Append("<?xml version=\"1.0\"?>\n");
            sb.Append("<ComicInfo>\n");
            sb.AppendFormat("<PageCount>{0}</PageCount>\n", files.Count);
            sb.Append("<Pages>\n");
            for (int i = 0; i < images.Count; i++)
                sb.AppendFormat("<Page Image=\"{0}\" ImageSize=\"{1}\" Key=\"{2}\" />\n", i, images.Values[i].Size, ComicInfo.XmlEncode(images.Keys[i]));
            sb.Append("</Pages>\n");
            sb.Append("</ComicInfo>\n");

            return new XPathDocument(new StringReader(sb.ToString()));
        }

        public Stream GetStream(string key)
        {
            string tempName = ComicInfo.CachePath + Path.DirectorySeparatorChar + "temp_package";
            HashAlgorithm sha256 = new SHA256Managed();
            using (FileStream fileStream = File.Open(tempName, FileMode.Create, FileAccess.Write, FileShare.Read))
                using (CryptoStream cryptoStream = new CryptoStream(fileStream, sha256, CryptoStreamMode.Write))
                    files[key].WriteTo(cryptoStream);

            string hashName = ComicInfo.CachePath + Path.DirectorySeparatorChar + ComicInfo.HashToString(sha256.Hash);
            if (!File.Exists(hashName))
                File.Move(tempName, hashName);
            else
                File.Delete(tempName);

            return File.Open(hashName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
        }

        public string GetKey(int index)
        {
            if (index >= images.Count)
                return null;
            return images.Keys[index];
        }
    }
}
